package org.speedy.tree;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class DependencyTree {

	private static int uniqufier = 1;

	private final Map<String, Node> allClasses = new HashMap<String, Node>();

	public void add(String className, String dependency, boolean dependencyIsModifiedLocally) {
		Node dependencyNode = getOrCreate(dependency);
		Node clazzNode = getOrCreate(className);

		dependencyNode.addDependant(clazzNode, dependencyIsModifiedLocally);
	}

	private Node getOrCreate(String name) {
		Node node = allClasses.get(name);
		if (node == null) {
			Node newNode = new Node(name);
			allClasses.put(name, newNode);
			return newNode;
		} else {
			return node;
		}
	}

	public Set<String> matchingLeaves(String postfix) {
		Set<String> leaves = new HashSet<String>();
		for (Node d : allClasses.values()) {
			if (d.isDependencyModifiedLocally && d.clazz.matches(postfix)) {
				leaves.add(d.clazz);
			}
		}
		return leaves;
	}

	private class Node {
		private final String clazz;
		private final Set<Node> dependants = new HashSet<Node>();
		private boolean isDependencyModifiedLocally;
		private long uniqueNumber;
		public Node(String clazz) {
			this.clazz = clazz;
		}
		public void addDependant(Node clazzNode, boolean isDependencyModifiedLocally) {
			long uniqueNumber = uniqufier++;
			clazzNode.dependencyIsModifiedLocally(isDependencyModifiedLocally, uniqueNumber);
			dependants.add(clazzNode);
		}
		private void dependencyIsModifiedLocally(
				boolean isDependencyModifiedLocally, long uniqueNumber) {
			if (this.uniqueNumber != uniqueNumber) {
				this.uniqueNumber = uniqueNumber;
			} else {
				return;
			}
			if (this.isDependencyModifiedLocally == false) {
				this.isDependencyModifiedLocally = isDependencyModifiedLocally;
				for (Node d : dependants) {
					d.dependencyIsModifiedLocally(isDependencyModifiedLocally, uniqueNumber);
				}
			}
		}
		public boolean equals(Object obj) {
			if (obj == null || obj.getClass() != this.getClass()) {
				return false;
			}
			return this.clazz.equals(((Node)obj).clazz);
		}
		public int hashCode() {
			return clazz.hashCode();
		}
		public String toString() {
			return clazz + ":" + isDependencyModifiedLocally;
		}
	}
}
